#-*- coding:utf-8 -*-
#!/usr/bin/env python
import time, datetime, _thread as thread
import sys
import os
from socket import *
from struct import *
import logging
import pymysql

import DB

strSQL_h = """INSERT INTO t_node_rawdata (RW_BN_SERIAL, RW_OCCUPANCY_TIME, RW_SPEED, RW_HEADTIME, RW_TIME) VALUES """
strSQL = """('%s', %d, %d, %d, '%s'),"""
strLog_h = """INSERT INTO t_comm_log (CDA_SERIAL, CDA_BIN, CDA_FUNC, CDA_TIME, CDA_BN_SERIAL) VALUES """
strLog = """('%04x', x'%s', '%02x', '%s', %s),"""

strMindata_h = """INSERT INTO t_node_mindata (MD_BN_SERIAL, MD_OCCUPANCY_TIME, MD_VEHICLE_COUNT, MD_SPEED, MD_SPEED_MAX, MD_HEADTIME, MD_HEADTIME_MAX, MD_SATURATION, MD_TIMEOPY, MD_PCU, MD_LANECODE, MD_TIME) VALUES """
strMindata = """('%s', %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, '%s'),"""

#ROAD CAPACITY TABLE
RCtable = {20:1400, 30:1600, 40:1650, 50:1700, 60:1800}

#LANE REDUCTION FACTOR TABLE
LRFtable = {1:1, 2:0.8, 3:0.7, 4:0.6, 5:0.5}

nodeTime = {}

sqlList = []
logList = []
dataMark = {}
addFrequncy = 20

def getLogger():
	logger = logging.getLogger('gg')

	dirpath = './log'
	#dirpath = 'd:\log'
	filename = 'db_%s.log' % time.strftime("%Y%m%d", time.localtime())
	handler = logging.FileHandler(os.path.join(dirpath, filename))

	formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')

	logger.addHandler(handler)
	logger.setLevel(logging.INFO)

	return logger

log = getLogger()


class storeData():
	minData = {}
	timeFlag = ''
	nodeMap = {}
	ringBuffer = []
	mysql = 0

	def __init__(self):
		self.mysql = DB.MysqlDB()
		self.timeFlag = time.strftime('%Y-%m-%d %H:%M:00')
		self.getMap()

	def getMap(self):
		strSQL = "select nm_bn_serial, nm_bs_serial, nm_lane from t_node_map"

		self.nodeMap = {}

		try:
			cur = self.mysql.send(strSQL)
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)
		
		for atom in cur:
			self.nodeMap[atom[0]] = atom

		self.mysql.commit()
		#print('node map:\n', self.nodeMap)
	
	def checkMindata(self):
		#print(self.timeFlag, '\t', time.strftime('%Y-%m-%d %H:%M:00'), '\t', len(self.minData))
		if time.strftime('%Y-%m-%d %H:%M:00') == self.timeFlag:
			return
		if len(self.minData) == 0:
			return

		#print('save mindata start')
		
		minSQL = strMindata_h
		try:
			for atom in self.minData:
				self.minData[atom][8] = int(self.minData[atom][1] / 60)

				if self.minData[atom][9] < 20:
					RC = 20
				elif self.minData[atom][9] >= 70:
					RC = 60
				else:
					RC = int(self.minData[atom][9] / 10) * 10
				try:
					self.minData[atom][9] = int((RCtable[RC] / 60) * LRFtable[self.minData[atom][10]])
				except Exception as err:
					strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
					log.info(strTmp)

				self.minData[atom][7] = int(self.minData[atom][2] * 100 / self.minData[atom][9])

				#print(self.minData[atom])
	
				minSQL = minSQL + (strMindata % (self.minData[atom][0], self.minData[atom][1], self.minData[atom][2], self.minData[atom][3], self.minData[atom][4], self.minData[atom][5], self.minData[atom][6], self.minData[atom][7], self.minData[atom][8], self.minData[atom][9], self.minData[atom][10], self.timeFlag))
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		minSQL = minSQL[0:-1]
		#print(minSQL)
		try:
			self.mysql.dml(minSQL)
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		self.timeFlag = time.strftime('%Y-%m-%d %H:%M:00')
		self.minData = {}


	def save(self, data):
		strFromat = "!%dB" % len(data)
		try:
			self.ringBuffer.extend(list(unpack(strFromat, data)))
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)
		self.parse()
		#print(len(self.ringBuffer))
		#print('.', end='')
		sys.stdout.flush()

	def dataCheck(self, mark, index):
		if mark not in dataMark:
			dataMark[mark] = index
			return True
		else:
			if dataMark[mark] == index:
				return False
			else:
				dataMark[mark] = index
				return True

	def dataLog(self, frameData = 0):
		if frameData == 0:
			if len(self.ringBuffer) < self.ringBuffer[7] + 9:
				del self.ringBuffer[0]
				return
			frameData = self.ringBuffer[0:self.ringBuffer[7] + 9]
			del self.ringBuffer[0:self.ringBuffer[7] + 9]
		
		tmp = ''
		for atom in frameData:
			tmp += '%02x' % atom
		dataFormat = "!%dB" % (frameData[7] + 9)
		frameFormat = "!3BH4B%dB" % frameData[7]

		try:
			data = unpack(frameFormat, pack(dataFormat, *frameData))
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		nodeID = ''
		if data[0] == 0xaa:
			if data[4] == 0x35:
				nodeID = "'" + ('%02x' % data[10]) + ('%02x' % data[11]) + ('%02x' % data[12]) + "'"
			elif data[4] == 0x81:
				if data[6] == 0x06:
					nodeID = "'" + ('%02x' % data[10]) + ('%02x' % data[11]) + ('%02x' % data[12]) + "'"
				else:
					nodeID = "'" + ('%02x' % data[9]) + ('%02x' % data[10]) + ('%02x' % data[11]) + "'"
			else:
				nodeID = 'NULL'
		else:
			nodeID = 'NULL'

		logList.append(strLog % (data[3], tmp, data[4], time.strftime('%Y-%m-%d %H:%M:%S'), nodeID))
		#print(logList)

		self.writeAll()

		return

	def parse(self):
		try:
			while True:
				if len(self.ringBuffer) < 10:
					break
				if self.ringBuffer[0] == 0xaa:
					if (self.ringBuffer[1] == 0xaa) and (len(self.ringBuffer) >= self.ringBuffer[7] + 9):
						self.getFrame()
						continue
					else:
						del self.ringBuffer[0]
						continue
				elif self.ringBuffer[0] == 0xab:
					if (self.ringBuffer[1] == 0xab) and (len(self.ringBuffer) >= self.ringBuffer[7] + 9):
						self.dataLog(0)
						continue
					else:
						del self.ringBuffer[0]
						continue
				del self.ringBuffer[0]
				continue
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

	def xor(self, data):
		res = 0x00
		for atom in data:
			res = res ^ atom
		return res

	def writeAll(self):
		global sqlList
		global logList
		if len(sqlList) > addFrequncy:
			tmp = strSQL_h
			for atom in sqlList:
				tmp += atom
			tmp = tmp[0:-1]
			#print(tmp)
			try:
				self.mysql.dml(tmp)
			except Exception as err:
				strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
				log.info(strTmp)

			sqlList = []

		if len(logList) > addFrequncy:
			tmp = strLog_h
			for atom in logList:
				tmp += atom
			tmp = tmp[0:-1]
			#print(tmp)
			try:
				self.mysql.dml(tmp)
			except Exception as err:
				strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
				log.info(strTmp)

			logList = []

		return

	def addMindata(self, dataList):
		#print('start add mindata')
		#print('nodeID:\t', dataList[0])
		if dataList[2] < 10 or dataList[2] > 100:
			speed = 0
		else:
			speed = dataList[2]
			
		if dataList[0] not in self.nodeMap:
			return

		if dataList[0] not in self.minData:
				self.minData[dataList[0]] = [dataList[0], dataList[1], 1, speed, speed, dataList[3], dataList[3], 0, 0, 0, self.nodeMap[dataList[0]][2]]

		else:
			self.minData[dataList[0]][1] = self.minData[dataList[0]][1] + dataList[1]

			self.minData[dataList[0]][2] = self.minData[dataList[0]][2] + 1

			if speed != 0:
				self.minData[dataList[0]][3] = (self.minData[dataList[0]][3] + speed) / 2

			if speed > self.minData[dataList[0]][4]:
				self.minData[dataList[0]][4] = speed

			self.minData[dataList[0]][5] = (self.minData[dataList[0]][5] + dataList[3]) / 2

			if dataList[3] > self.minData[dataList[0]][6]:
				self.minData[dataList[0]][6] = dataList[3]

		#print(self.minData[dataList[0]])

	def singleVehicle(self, frameData):
		dataFormat = "!%dB" % (frameData[7] + 9)
		frameFormat = "!HBHBH2BIH4B"

		try:
			data = unpack(frameFormat, pack(dataFormat, *frameData))
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		dataList = ['%06x' % (data[7] & 0xFFFFFF), data[8], data[9], 0]

		if dataList[0] in nodeTime:
			dataList[3] = int(time.time()) - nodeTime[dataList[0]]
		else:
			dataList[3] = 0

		nodeTime[dataList[0]] = int(time.time())

		if self.dataCheck(data[2], data[7] >> 24) is not True:
			return
		#occupancy time > 1 min
		if dataList[1] > 6000:
			return

		try:
			self.addMindata(dataList)
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		sqlList.append(strSQL % (dataList[0], dataList[1], dataList[2], dataList[3], time.strftime('%Y-%m-%d %H:%M:%S')))

		self.writeAll()

		return

	def addSigleVehicle(self, frameData):
		dataFormat = "!%dB" % (frameData[7] + 9)
		frameFormat = "!HBH4BIHBI"

		try:
			data = unpack(frameFormat, pack(dataFormat, *frameData))
			#print('-', end='')
			sys.stdout.flush()
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		dataList = ['%06x' % (data[7] & 0xFFFFFF), data[8], data[9], 0]

		if dataList[0] in nodeTime:
			dataList[3] = int(time.time()) - nodeTime[dataList[0]]
		else:
			dataList[3] = 0

		nodeTime[dataList[0]] = int(time.time())

		if self.dataCheck(data[2], data[7] >> 24) is not True:
			return
		#occupancy time > 1 min
		if dataList[1] > 6000:
			return

		try:
			self.addMindata(dataList)
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		sqlList.append(strSQL % (dataList[0], dataList[1], dataList[2], dataList[3], time.strftime('%Y-%m-%d %H:%M:%S')))

		self.writeAll()

		return
		
	def dynamicDtails(self, frameData):
		dataFormat = "!%dB" % (frameData[7] + 9)
		frameFormat = "!HBH5BI3H3B"
		try:
			data = unpack(frameFormat, pack(dataFormat, *frameData))
		except Exception as err:
			#print('[', sys._getframe().f_code.co_name, ':', sys._getframe().f_lineno, ']', err)
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		print('station time:\t%02d:%02d:02d\tserver time:\t%s' % (data[-3], data[-2], data[-1], time.strftime('%H:%M:%S')))

		if data[10] == 0:
			speed = 0
		else:
			speed = int((6 * 360) / data[10])
		try:
			dataList = ['%06x' % (data[8] & 0xFFFFFF), data[10], speed, 0]
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		if dataList[0] in nodeTime:
			dataList[3] = int(time.time()) - nodeTime[dataList[0]]
		else:
			dataList[3] = 0

		nodeTime[dataList[0]] = int(time.time())

		#occupancy time > 1 min
		if dataList[1] > 6000:
			return

		try:
			self.addMindata(dataList)
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)

		sqlList.append(strSQL % (dataList[0], dataList[1], dataList[2], dataList[3], time.strftime('%Y-%m-%d %H:%M:%S')))
		#print(sqlList)

		self.writeAll()

		return


	def getFrame(self):
		frameData = self.ringBuffer[0:self.ringBuffer[7] + 9]
		del self.ringBuffer[0:self.ringBuffer[7] + 9]
		
####################################################################
##		Get one frame data from the ringBuffer
####################################################################
		
		try:
			self.checkMindata()
		except Exception as err:
			strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
			log.info(strTmp)
		
		if self.xor(frameData) != 0:
			return

		"""
		print("%s  " % now(), end='')
		for atom in frameData:
			print("%02x" % atom, end=' ')
		print('')
		"""
		self.dataLog(frameData)
		
		##	function code: 0x30		single vehicle
		if frameData[5] == 0x30:
			try:
				self.singleVehicle(frameData)
			except Exception as err:
				strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
				log.info(strTmp)
		
		elif frameData[5] == 0x35:
			try:
				self.dynamicDtails(frameData)
			except Exception as err:
				strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
				log.info(strTmp)
		
		elif frameData[5] == 0x36:
			try:
				self.addSigleVehicle(frameData)
			except Exception as err:
				strTmp = "[%s][%-10s:line %4d] %s" % (time.strftime('%Y-%m-%d %H:%M:%S'), sys._getframe().f_code.co_name, sys._getframe().f_lineno, err)
				log.info(strTmp)
		
		else:
			pass



def now():
	return time.ctime(time.time())
	

